package gupao_edu_spring_webmvc.web.servlet;

import gupao_edu_spring_context.context.MYApplicationContext;
import gupao_edu_spring_context.stereotype.MYController;
import gupao_edu_spring_webmvc.web.bind.annotation.MYRequestMapping;
import gupao_edu_spring_webmvc.web.constants.Constants;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName:
 * @Description:
 * @Auther: zhouych
 * @Date: 2019/5/9 19:50
 */
public class MYDispatcherServlet extends HttpServlet {

    private MYApplicationContext context;

    public static List<MYHandlerMapping> handlerMappings = new ArrayList<>();

    private Map<MYHandlerMapping,MYHandlerAdapter> handlerAdapters = new HashMap<MYHandlerMapping,MYHandlerAdapter>();

    private List<MYViewResolver> viewResolvers = new ArrayList<MYViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write(Constants.ERROR);
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1、根据请求url获取handlerMapping
        MYHandlerMapping handler = getHandler(req);
        if (handler==null) {
            processDispatchResult(req,resp,new MYModelAndView(Constants.NOT_FOUND));
            return;
        }

        //2、参数处理
        MYHandlerAdapter ha = getHandlerAdapter(handler);

        //3、调用方法、并返回modelAndView
        MYModelAndView mv = ha.handle(req,resp,handler);

        //4、输出html
        processDispatchResult(req, resp, mv);
    }

    private MYHandlerAdapter getHandlerAdapter(MYHandlerMapping handler) {

        if(this.handlerAdapters.isEmpty()){return null;}
        MYHandlerAdapter ha = this.handlerAdapters.get(handler);
        if(ha.supports(handler)){
            return ha;
        }
        return null;
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, MYModelAndView modelAndView) throws Exception {
        if(null == modelAndView){return;}

        if(this.viewResolvers.isEmpty()){return;}

        for (MYViewResolver viewResolver : this.viewResolvers) {
            MYView view = viewResolver.resolveViewName(modelAndView.getViewName(),null);
            view.render(modelAndView.getModel(),req,resp);
            return;
        }
    }

    private MYHandlerMapping getHandler(HttpServletRequest req) {
        if (this.handlerMappings.isEmpty()) return null;
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");

        for (MYHandlerMapping handler:this.handlerMappings){
            Matcher matcher = handler.getPattern().matcher(url);
            if(!matcher.matches()) continue;
            return handler;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、初始化ApplicationContext
        context = new MYApplicationContext(config.getInitParameter(Constants.CONTEXT_CONFIG_LOCATION));
        //2、初始化Spring MVC 九大组件
        initStrategies(context);
    }

    private void initStrategies(MYApplicationContext context) {
       /* //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);*/


        //handlerMapping，必须实现
        initHandlerMappings(context);
        //初始化参数适配器，必须实现
        initHandlerAdapters(context);
       /* //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);*/


        //初始化视图转换器，必须实现
        initViewResolvers(context);
        //参数缓存器
        //initFlashMapManager(context);
    }


    private void initViewResolvers(MYApplicationContext context) {

        /*//拿到模板的存放目录
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i ++) {
            //这里主要是为了兼容多模板，所有模仿Spring用List保存
            //在我写的代码中简化了，其实只有需要一个模板就可以搞定
            //只是为了仿真，所有还是搞了个List
            this.viewResolvers.add(new GPViewResolver(templateRoot));
        }*/

    }

    private void initHandlerAdapters(MYApplicationContext context) {
        for (MYHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new MYHandlerAdapter());
        }
    }

    private void initHandlerMappings(MYApplicationContext context) {

        String [] beanNames = context.getBeanDefinitionNames();

        try {

            for (String beanName : beanNames) {

                Object controller = context.getBean(beanName);

                Class<?> clazz = controller.getClass();

                if(!clazz.isAnnotationPresent(MYController.class)){
                    continue;
                }

                String baseUrl = "";
                //获取Controller的url配置
                if(clazz.isAnnotationPresent(MYRequestMapping.class)){
                    MYRequestMapping requestMapping = clazz.getAnnotation(MYRequestMapping.class);
                    baseUrl = requestMapping.value();
                }

                //获取Method的url配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {

                    //没有加RequestMapping注解的直接忽略
                    if(!method.isAnnotationPresent(MYRequestMapping.class)){ continue; }

                    //映射URL
                    MYRequestMapping requestMapping = method.getAnnotation(MYRequestMapping.class);

                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);

                    this.handlerMappings.add(new MYHandlerMapping(pattern,controller,method));
                }


            }
        }catch (Exception e){
            e.printStackTrace();
        }


    }
}
